package service

import (
	"context"
	"encoding/json"
	"gitee.com/crack007/goose"
	"gitee.com/crack007/goose/log"
	"github.com/coreos/etcd/clientv3"
	"github.com/crack007/go-im/src/common/config"
	"github.com/crack007/go-im/src/common/model"
	"github.com/crack007/go-im/src/common/util"
	"strings"
	"time"
)

type etcdService struct {
	client  *clientv3.Client
	Ctx     context.Context
	NodeTTL int64
}

func (s *etcdService) Init(ctx context.Context) {
	s.Ctx = ctx
	etcdConfig := config.GetEtcdConfig()
	log.GetLogger().Debug("Connect etcd-> %s", strings.Join(etcdConfig.Endpoints, ","))
	s.NodeTTL = 3
	client, err := clientv3.New(clientv3.Config{
		Endpoints:   etcdConfig.Endpoints,
		DialTimeout: time.Duration(etcdConfig.Dialtimeout) * time.Millisecond,
	})
	if err != nil {
		goose.GetLogger().Error(err.Error())
		return
	}
	s.client = client
	// 开协程监听退出信号
	go func() {
		<-s.Ctx.Done()
		goose.GetLogger().Debug("Etcd client close")
		s.client.Close()
	}()
}

// 注册服务
func (s *etcdService) Register(etcdModel *model.EtcdModel) error {
	goose.GetLogger().Debug("注册节点：%s %s", etcdModel.Node.Id, etcdModel.Node.Ip)
	// 创建续租
	grant, err := s.client.Grant(s.Ctx, s.NodeTTL)
	if err != nil {
		return err
	}
	lease := clientv3.NewLease(s.client)
	leaseID := grant.ID
	err = s.lease(util.GetNodePath(etcdModel.Node.Id), leaseID, etcdModel)
	if err != nil {
		return err
	}
	leaseKeepAliveResponses, err := lease.KeepAlive(s.Ctx, leaseID)
	if err != nil {
		return err
	}
	retry := 0
	for {
		select {
		case <-s.Ctx.Done():
			goose.GetLogger().Debug("退出续租")
			return nil
		case response := <-leaseKeepAliveResponses:
			if response == nil {
				// 续租失败做次数限制
				goose.GetLogger().Error("续租失败")
				retry++
				if retry > 10 {
					return nil
				}
			}
		}
	}
}

// 监控服务
func (s *etcdService) Watch(serviceNode string, callback func(nodeName string, event string, etcdModel *model.EtcdModel)) {
	goose.GetLogger().Debug("开始监控")
	watch := s.client.Watch(s.Ctx, serviceNode, clientv3.WithPrefix())
	for response := range watch {
		for _, ev := range response.Events {
			var etcdModel *model.EtcdModel
			json.Unmarshal(ev.Kv.Value, &etcdModel)
			callback(util.GetNodeName(string(ev.Kv.Key), serviceNode), ev.Type.String(), etcdModel)
		}
	}
}

func (s *etcdService) ListNode(serviceNode string, callback func(etcdModel *model.EtcdModel)) {
	goose.GetLogger().Debug("开始读取节点")
	response, err := s.client.Get(s.Ctx, serviceNode, clientv3.WithPrefix())
	if err != nil {
		goose.GetLogger().Error(err.Error())
		return
	}
	if len(response.Kvs) > 0 {
		for _, kv := range response.Kvs {
			var etcdModel *model.EtcdModel
			json.Unmarshal(kv.Value, &etcdModel)
			callback(etcdModel)
		}
	}
}

func (s *etcdService) lease(key string, id clientv3.LeaseID, etcdModel *model.EtcdModel) error {
	kv := clientv3.NewKV(s.client)
	_, err := kv.Put(context.TODO(), key, util.ToJson(etcdModel), clientv3.WithLease(id))
	if err != nil {
		return err
	}
	return nil
}
